package org.infodavid.common.model;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import javax.persistence.PersistenceException;

import org.infodavid.common.persistence.IDataObject;
import org.infodavid.common.persistence.IQueryParameter;
import org.infodavid.common.persistence.PersistenceSession;
import org.infodavid.common.persistence.QueryParameterFactory;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;

/**
 * The Class AbstractTest.
 * @param <T> the generic type
 */
@SuppressWarnings({
  "rawtypes"
})
public abstract class AbstractBeanTest<T extends IDataObject> extends AbstractPersistenceTest {

  /** The test data. */
  private final List<T> testData = new ArrayList<T>();

  /**
   * Instantiates a new abstract bean test.
   */
  public AbstractBeanTest() {
    super();
  }

  /**
   * Instantiates a new abstract bean test.
   * @param parentTest the main test
   */
  public AbstractBeanTest(final AbstractPersistenceTest parentTest) {
    super(parentTest);
  }

  /**
   * Returns the number of rows.
   * @return the number of rows
   * @throws PersistenceException the persistence exception
   */
  protected long count() throws PersistenceException {
    final Object obj =
        getSession().executeQuery(getEntityClass(), getEntityName() + ".selectAll.count", null);

    return obj instanceof Number ? ((Number)obj).longValue() : 0;
  }

  /**
   * Gets the test data.
   * @return the test data
   */
  public List<T> getTestData() {
    return testData;
  }

  /**
   * Creates the.
   * @param count the count
   * @return the collection
   * @throws Exception the exception
   */
  public final List<T> create(final int count) throws Exception {
    return doCreate(count);
  }

  /**
   * Creates the.
   * @param count the count
   * @return the collection
   * @throws Exception the exception
   */
  protected abstract List<T> doCreate(final int count) throws Exception;

  /*
   * (non-Javadoc)
   * @see org.infodavid.common.model.AbstractPersistenceTest#setUp()
   */
  @Override
  @Before
  public void setUp() throws Exception {
    super.setUp();

    if (getParent() == null) {
      if (testData.isEmpty()) {
        final long initialCount = count();
        final List<T> items = create(ITEMS_TO_INSERT);
        final List<T> inserted = getSession().insertEntities(items, null);

        Assert.assertEquals("Not all the items were inserted", ITEMS_TO_INSERT, count()
            - initialCount);

        testData.addAll(inserted);
      }
    }
  }

  /*
   * (non-Javadoc)
   * @see org.infodavid.common.model.AbstractPersistenceTest#tearDown()
   */
  @Override
  @After
  public void tearDown() throws Exception {
    if (getParent() == null) {}

    super.tearDown();
  }

  /**
   * Populate.
   * @param count the count
   * @return the collection
   * @throws Exception the exception
   */
  public List<T> populate(final int count) throws Exception {
    final long initialCount = count();
    final List<T> items = create(count);
    final List<T> inserted = getSession().insertEntities(items, null);

    Assert.assertEquals("Not all the items were inserted", count, count() - initialCount);

    return inserted;
  }

  /**
   * Gets the entity class.
   * @return the entity class
   */
  public abstract Class<? extends T> getEntityClass();

  /**
   * Gets the entity name.
   * @return the entity name
   */
  public abstract String getEntityName();

  /**
   * Test insert entity.
   * @throws Exception the exception
   */
  @Test
  public final void testInsertEntity() throws Exception {
    Assert.assertEquals("Not all insertedObjects has been inserted", ITEMS_TO_INSERT, getTestData()
        .size());

    final long result = count();

    Assert.assertEquals("Not all insertedObjects has been inserted", ITEMS_TO_INSERT, result);
  }

  /**
   * Test remove entities.
   * @throws Exception the exception
   */
  @Test
  public final void testRemoveEntities() throws Exception {
    final PersistenceSession session = getSession();
    final List<IQueryParameter> params = new ArrayList<IQueryParameter>();
    final IQueryParameter param;
    final long count = count();

    try {// TOD fix test code
      param = QueryParameterFactory.getSingleton().borrow();
    }
    catch (final Throwable e) {
      e.printStackTrace();

      throw new Error(e);
    }
    try {
      final Collection<T> inserted = getTestData();

      param.setName(IDataObject.ARCHIVED).setValue(Boolean.FALSE);
      params.add(param);

      session.removeEntities(inserted, null);
      session.getTransaction().commit();

      Assert.assertEquals("Not all insertedObjects has been inserted", count - ITEMS_TO_INSERT,
          count());
    }
    finally {
      QueryParameterFactory.getSingleton().release(param);
    }
  }

  /**
   * Test remove entity.
   * @throws Exception the exception
   */
  @Test
  public void testRemoveEntity() throws Exception {
    final PersistenceSession session = getSession();
    final Collection<T> inserted = getTestData();
    final T item = inserted.iterator().next();
    final Serializable key = item.getKey();

    session.removeEntity(item);
    session.getTransaction().commit();
    session.getTransaction().begin();

    Assert.assertNull("Not all insertedObjects has been inserted",
        session.findEntity(getEntityClass(), key));
  }

  /**
   * Test update entities.
   * @throws Exception the exception
   */
  @Test
  public final void testUpdateEntities() throws Exception {
    final PersistenceSession session = getSession();
    final Collection<T> inserted = getTestData();

    for (final T data : inserted) {
      if (!data.isArchived()) {
        data.setArchived(true);
      }
    }

    session.updateEntities(inserted, null);
    session.getTransaction().commit();

    final Collection<? extends T> result =
        session.findEntities(getEntityClass(), getEntityName() + ".selectAll", null);

    Assert.assertNotNull("Result is null", result);

    for (final T data : result) {
      if (inserted.contains(data)) {
        Assert.assertTrue("Entity not updated", data.isArchived());
      }
    }
  }

  /**
   * Test update entity.
   * @throws Exception the exception
   */
  @Test
  public final void testUpdateEntity() throws Exception {
    final PersistenceSession session = getSession();
    final Collection<T> inserted = getTestData();

    for (final T data : inserted) {
      if (!data.isArchived()) {
        data.setArchived(true);
        session.updateEntity(data);
      }
    }

    session.getTransaction().commit();

    final Collection<? extends T> result =
        session.findEntities(getEntityClass(), getEntityName() + ".selectAll", null);

    Assert.assertNotNull("Result is null", result);

    for (final T data : result) {
      if (inserted.contains(data)) {
        Assert.assertTrue("Entity not updated", data.isArchived());
      }
    }
  }
}
